<?php

declare(strict_types = 1);

namespace Application\Queue;

use Swoole\Http\Request;
use Swoole\WebSocket\Frame;
use Swoole\Websocket\Server as WebsocketServer;

/**
 * Class Server
 *
 * @package Application\Queue
 */
class Server
{
    /** @var string $host */
    protected string $host;
    /** @var int $port */
    protected int $port;

    /** @var WebsocketServer $webSocketServer */
    protected WebsocketServer $webSocketServer;

    /** @var Storage $storage Message cache. */
    protected Storage $storage;

    /**
     * Server constructor.
     *
     * @param string $host
     * @param int $port
     */
    public function __construct(string $host, int $port)
    {
        $this->host = $host;
        $this->port = $port;

        $this->initialize();
    }

    /**
     * Initialize.
     */
    protected function initialize()
    {
        $this->webSocketServer = new WebsocketServer($this->host, $this->port);
        $this->webSocketServer->on('start', [$this, 'onStart']);
        $this->webSocketServer->on('open', [$this, 'onOpen']);
        $this->webSocketServer->on('message', [$this, 'onMessage']);
        $this->webSocketServer->on('close', [$this, 'onClose']);

        $this->storage = new Storage();
    }

    /**
     * Start.
     */
    public function start()
    {
        $this->webSocketServer->start();
    }

    /**
     * Shutdown.
     */
    public function shutdown()
    {
        $this->webSocketServer->shutdown();
    }

    /**
     * Server start event register.
     *
     * @param WebsocketServer $server
     */
    public function onStart(WebsocketServer $server)
    {
        echo "Queue Server is started at ws://{$this->host}:{$this->port}\n";
    }

    /**
     * Server the connection open event register.
     *
     * @param WebsocketServer $server
     * @param Request $request
     */
    public function onOpen(WebsocketServer $server, Request $request)
    {
        echo "Connection open: {$request->fd}\n";
    }

    /**
     * Server receive message event register.
     *
     * @param WebsocketServer $server
     * @param Frame $frame
     */
    public function onMessage(WebsocketServer $server, Frame $frame)
    {
        echo "Received message: {$frame->data}\n";

        try {
            $message = Message::parseMessage($frame->data);

            $this->storage->push($message->getQueue(), $message->getMessage());

            $server->push($frame->fd, 'ack');
        } catch (\Exception $exception) {
            $server->push($frame->fd, 'nack');
        }
    }

    /**
     * Server the connection close event register.
     *
     * @param WebsocketServer $server
     * @param int $fd
     */
    public function onClose(WebsocketServer $server, int $fd)
    {
        echo "Connection close: {$fd}\n";
    }
}